/*
 * @ : Copyright (c) 2021 Phytium Information Technology, Inc. 
 *  
 * SPDX-License-Identifier: Apache-2.0.
 * 
 * @Date: 2021-07-20 08:13:54
 * @LastEditTime: 2021-08-16 16:01:38
 * @Description:  This files is for 
 * 
 * @Modify History: 
 *  Ver   Who        Date         Changes
 * ----- ------     --------    --------------------------------------
 */
#include <string.h>
#include "kernel.h"
#include "ft_assert.h"
#include "kernel.h"
#include "qspi.h"
#include "qspi_hw.h"

/**
 * @name: NorFlashReadId
 * @msg: Read JeDev id (for cmd QSPI_FLASH_CMD_RDID 0x9F)
 * @return {*}
 * @param {QSpiCtrl} *pCtrl
 * @param {u32} opCode
 * @param {u8} *pBuf
 * @param {size_t} len
 */
static u32 NorFlashReadId(QSpiCtrl *pCtrl, u32 opCode, u8 *pBuf, size_t len)
{
    FT_ASSERTZERONUM(pCtrl && pBuf);
    u32 ret = QSPI_SUCCESS;
    u32 cmdReg = 0;

    cmdReg |= QSPI_CMD_PORT_CMD_MASK & QSPI_CMD_PORT_CMD(opCode);
    cmdReg |= QSPI_CMD_PORT_DATA_TRANS(0x1) |
              QSPI_CMD_PORT_P_BUFFER(0x1);
    cmdReg |= QSPI_CMD_PORT_CLK_SEL_MASK &
              QSPI_CMD_PORT_CLK_SEL(pCtrl->config.clkDiv);
    cmdReg |= QSPI_CMD_PORT_CS_MASK &
              QSPI_CMD_PORT_CS(pCtrl->config.channel);

    QSPI_CMD_WRITE(pCtrl, cmdReg);
    QSPI_LD_WRITE(pCtrl, 0x0);
    ret |= QSpiGetLdPortData(pCtrl, pBuf, len);
    return ret;
}

/**
 * @name: NorFlashReadSr1
 * @msg: 
 * @return {*}
 * @param {QSpiCtrl} *pCtrl
 * @param {u32} opCode
 * @param {u8} *pBuf
 * @param {size_t} len
 */
static u32 NorFlashReadSr1(QSpiCtrl *pCtrl, u32 opCode, u8 *pBuf, size_t len)
{
    FT_ASSERTZERONUM(pCtrl && pBuf);
    u32 ret = QSPI_SUCCESS;
    u32 cmdReg = 0;

    cmdReg |= QSPI_CMD_PORT_CMD_MASK & QSPI_CMD_PORT_CMD(opCode);
    cmdReg |= QSPI_CMD_PORT_DATA_TRANS(0x1);
    cmdReg |= QSPI_CMD_PORT_RW_NUM_MASK & QSPI_CMD_PORT_RW_NUM(len);
    cmdReg |= QSPI_CMD_PORT_CLK_SEL_MASK &
              QSPI_CMD_PORT_CLK_SEL(pCtrl->config.clkDiv);
    cmdReg |= QSPI_CMD_PORT_CS_MASK &
              QSPI_CMD_PORT_CS(pCtrl->config.channel);

    QSPI_CMD_WRITE(pCtrl, cmdReg);
    QSPI_LD_WRITE(pCtrl, 0x0);
    ret |= QSpiGetLdPortData(pCtrl, pBuf, len);
    return ret;
}

u32 NorFlashReadReg(QSpiCtrl *pCtrl, u32 opCode, u8 *pBuf, size_t len)
{
    FT_ASSERTZERONUM(pCtrl && pBuf);
    u32 ret = QSPI_SUCCESS;
    u32 cmdReg = 0;

    if (FT_COMPONENT_IS_READY != pCtrl->isReady)
    {
        QSPI_ERROR("Nor flash not ready !!!");
        ret |= QSPI_NOT_READY;
        return ret;
    }

    switch (opCode)
    {
    case QSPI_FLASH_CMD_RDID:
        ret = NorFlashReadId(pCtrl, opCode, pBuf, len);
        return ret;
        break;
    case QSPI_FLASH_CMD_RDSR1:
        ret = NorFlashReadSr1(pCtrl, opCode, pBuf, len);
        return ret;
        break;
    default:
        break;
    }

    cmdReg |= QSPI_CMD_PORT_CMD_MASK & QSPI_CMD_PORT_CMD(opCode);
    cmdReg |= QSPI_CMD_PORT_DATA_TRANS(0x1) | QSPI_CMD_PORT_P_BUFFER(0x1);
    cmdReg |= QSPI_CMD_PORT_CLK_SEL_MASK &
              QSPI_CMD_PORT_CLK_SEL(pCtrl->config.clkDiv);
    cmdReg |= QSPI_CMD_PORT_CS_MASK &
              QSPI_CMD_PORT_CS(pCtrl->config.channel);

    QSPI_CMD_WRITE(pCtrl, cmdReg);
    QSPI_LD_WRITE(pCtrl, 0x0);
    ret |= QSpiGetLdPortData(pCtrl, pBuf, len);
    return ret;
}

static u32 NorFlashReadSfdp(QSpiCtrl *pCtrl, u32 opCode, u32 addr, u8 *pBuf, size_t len)
{
    FT_ASSERTZERONUM(pCtrl && pBuf);
    u32 ret = QSPI_SUCCESS;
    u32 cmdReg = 0;

    cmdReg |= QSPI_CMD_PORT_CMD_MASK & QSPI_CMD_PORT_CMD(opCode);
    cmdReg |= QSPI_CMD_PORT_DATA_TRANS(0x1) | QSPI_CMD_PORT_P_BUFFER(0x1) |
              QSPI_CMD_PORT_CMD_ADDR(0x1);
    cmdReg |= QSPI_CMD_PORT_CLK_SEL_MASK &
              QSPI_CMD_PORT_CLK_SEL(pCtrl->config.clkDiv);
    cmdReg |= QSPI_CMD_PORT_CS_MASK &
              QSPI_CMD_PORT_CS(pCtrl->config.channel);

    QSPI_CMD_WRITE(pCtrl, cmdReg);
    QSPI_CMD_ADDR_WRITE(pCtrl, addr);
    QSPI_LD_WRITE(pCtrl, 0x0);
    ret |= QSpiGetLdPortData(pCtrl, pBuf, len);
    return ret;
}

/**
 * @name: NorFlashMemcpy
 * @msg: same as memcpy, norflash qspi support read lenght align by 4, this 
 *      function handle those buffer not aligned by 4-byts
 * @return {*}
 * @param {void} *pDst
 * @param {void} *pSrc
 * @param {size_t} len
 */
static void NorFlashMemcpy(void *pDst, void *pSrc, size_t len)
{
    /* addr of copy dst or src might be zero */
    //FT_ASSERTVOID(pDst && pSrc);
    size_t loop;
    intptr srcAddr = (intptr)pSrc; /* conver to 32/64 bit addr */
    intptr dstAddr = (intptr)pDst;
    const size_t cnt = len / 4;    /* cnt number of 4-bytes need copy  */
    const size_t remain = len % 4; /* remain number of 1-byte not aligned */
    u8 alignBuf[4];

    if (0 == len)
    {
        return;
    }

    if (IS_ALIGNED(srcAddr, 4)) /* if copy src is aligned by 4 bytes */
    {
        /* read 4-bytes aligned buf part */
        for (loop = 0; loop < cnt; loop++)
        {
            *(u32 *)dstAddr = *(volatile u32 *)(srcAddr);
            srcAddr += 4;
            dstAddr += 4;
        }

        if (remain > 0)
        {
            *(u32 *)alignBuf = *(volatile u32 *)(srcAddr);
        }

        /* read remain un-aligned buf byte by byte */
        for (loop = 0; loop < remain; loop++)
        {
            *(u8 *)dstAddr = alignBuf[loop];
            dstAddr += 1;
        }
    }
    else /* if copy src is not aligned */
    {
        /* read byte by byte */
        for (loop = 0; loop < len; loop++)
        {
            *(u8 *)dstAddr = *(volatile u8 *)(srcAddr);
            dstAddr += 1;
            srcAddr += 1;
        }
    }

    return;
}

u32 NorFlashRead(QSpiCtrl *pCtrl, u32 opCode, u32 inChipAddr, u8 *pBuf, size_t len)
{
    QSPI_INFO("addr 0x%p, 0x%p", pCtrl, pBuf);
    FT_ASSERTZERONUM(pCtrl && pBuf);
    u32 ret = QSPI_SUCCESS;
    u32 rdCfg = 0;
    u32 addr = inChipAddr + pCtrl->config.channel * pCtrl->flashSize;

    if (FT_COMPONENT_IS_READY != pCtrl->isReady)
    {
        QSPI_ERROR("Nor flash not ready !!!");
        ret |= QSPI_NOT_READY;
        return ret;
    }

    rdCfg |= QSPI_RD_CFG_CMD_MASK & QSPI_RD_CFG_CMD(opCode);
    rdCfg |= QSPI_RD_CFG_D_BUFFER(0x1);
    rdCfg |= QSPI_RD_CFG_SCK_SEL_MASK &
             QSPI_RD_CFG_SCK_SEL(pCtrl->config.clkDiv);
    rdCfg &= ~QSPI_RD_CFG_TRANSFER_MASK;
    rdCfg |= QSPI_RD_CFG_TRANSFER_MASK &
             QSPI_RD_CFG_TRANSFER(pCtrl->config.transMode);

    rdCfg &= ~QSPI_RD_CFG_ADDR_SEL_MASK;
    switch (opCode)
    {
    case QSPI_FLASH_CMD_READ:
    case QSPI_FLASH_CMD_FAST_READ:
    case QSPI_FLASH_CMD_QIOR:
    case QSPI_FLASH_CMD_QOR:
        rdCfg |= QSPI_RD_CFG_ADDR_SEL(QSPI_ADDR_SEL_3);
        break;
    case QSPI_FLASH_CMD_4READ:
    case QSPI_FLASH_CMD_4FAST_READ:
    case QSPI_FLASH_CMD_4QOR:
    case QSPI_FLASH_CMD_4QIOR:
        rdCfg |= QSPI_RD_CFG_ADDR_SEL(QSPI_ADDR_SEL_4);
        break;
    case QSPI_FLASH_CMD_SFDP:
        rdCfg |= QSPI_RD_CFG_ADDR_SEL(QSPI_ADDR_SEL_3);
        ret |= NorFlashReadSfdp(pCtrl, opCode, addr, pBuf, len);
        return ret;
        break;
    default:
        ret |= QSPI_NOT_SUPPORT;
        return ret;
        break;
    }

    if ((QSPI_TRANSFER_1_1_1 == pCtrl->config.addrMode) ||
        (QSPI_TRANSFER_1_1_4 == pCtrl->config.addrMode))
    {
        rdCfg |= QSPI_RD_CFG_LATENCY(0x1);
        rdCfg &= ~QSPI_RD_CFG_DUMMY_MASK;
        rdCfg |= QSPI_RD_CFG_DUMMY(0x7);
    }

    QSPI_INFO("read cfg : 0x%lx", rdCfg);
    QSPI_RD_CFG_WRITE(pCtrl, rdCfg);

    QSPI_INFO("copy from 0x%p to 0x%p, len 0x%d",
              (void *)(pCtrl->config.memStart + addr),
              pBuf,
              len);

    /* 4字节对齐拷贝 */
    NorFlashMemcpy(pBuf, (void *)(pCtrl->config.memStart + addr), len);

    /* 不对齐拷贝 */
    return ret;
}

u32 NorFlashWrite(QSpiCtrl *pCtrl, u32 opCode, u32 inChipAddr, const u8 *pBuf, size_t len)
{
    FT_ASSERTZERONUM(pCtrl && pBuf);
    u32 ret = QSPI_SUCCESS;
    u32 wrCfg = 0;
    u32 loop;
    const u32 mask = (u32)GENMASK(1, 0);
    u32 addr = inChipAddr + pCtrl->config.channel * pCtrl->flashSize;
    u8 tmp[8] = {0};

    if (FT_COMPONENT_IS_READY != pCtrl->isReady)
    {
        QSPI_ERROR("Nor flash not ready !!!");
        ret |= QSPI_NOT_READY;
        return ret;
    }

    if (mask & addr)
    {
        QSPI_ERROR("nor flash write addr (0x%lx) not allgined !!!", addr);
        return QSPI_NOT_ALLIGN;
    }

    wrCfg |= QSPI_WR_CFG_CMD_MASK & QSPI_WR_CFG_CMD(opCode);
    wrCfg |= QSPI_WR_CFG_MODE(0x1);
    wrCfg |= QSPI_WR_CFG_SCK_SEL_MASK &&
             QSPI_WR_CFG_SCK_SEL(pCtrl->config.clkDiv);

    wrCfg &= ~QSPI_WR_CFG_ADDRSEL_MASK;
    switch (opCode)
    {
    case QSPI_FLASH_CMD_PP:
    case QSPI_FLASH_CMD_QPP:
        wrCfg |= QSPI_WR_CFG_ADDRSEL(QSPI_ADDR_SEL_3);
        break;
    case QSPI_FLASH_CMD_4PP:
    case QSPI_FLASH_CMD_4QPP:
        wrCfg |= QSPI_WR_CFG_ADDRSEL(QSPI_ADDR_SEL_4);
        break;
    default:
        ret |= QSPI_NOT_SUPPORT;
        return ret;
        break;
    }

    QSPI_WR_CFG_WRITE(pCtrl, wrCfg);

    /* write alligned data into memory space */
    for (loop = 0; loop < (len >> 2); loop++)
    {
        QSPI_DAT_WRITE(pCtrl->config.memStart + addr + 4 * loop,
                       *(u32 *)(pBuf + 4 * loop));
    }

    /* write not alligned data into memory space */
    if (len & mask)
    {
        addr = addr + (len & ~mask);
        ret |= NorFlashRead(pCtrl, QSPI_FLASH_CMD_READ, addr, &tmp[0], 4);
        memcpy(tmp, pBuf + (len & ~mask), len & mask);
        QSPI_DAT_WRITE(pCtrl->config.memStart + addr, *(u32 *)(tmp));
    }

    QSPI_FLUSH(pCtrl);
    ret |= QSpiWaitForCmd(pCtrl);

    return ret;
}

u32 NorFlashErase(QSpiCtrl *pCtrl, u32 opCode, u32 offset)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 ret = QSPI_SUCCESS;
    u32 cmdReg = 0;
    u32 addr = offset;

    if (FT_COMPONENT_IS_READY != pCtrl->isReady)
    {
        QSPI_ERROR("Nor flash not ready !!!");
        ret |= QSPI_NOT_READY;
        return ret;
    }

    QSpiEnableWrite(pCtrl);
    cmdReg |= QSPI_CMD_PORT_CMD_MASK & QSPI_CMD_PORT_CMD(opCode);
    cmdReg |= QSPI_CMD_PORT_CLK_SEL_MASK &
              QSPI_CMD_PORT_CLK_SEL(pCtrl->config.clkDiv);

    cmdReg &= ~QSPI_CMD_PORT_ADDR_SEL_MASK; /* clear addr select bit */
    switch (opCode)
    {
    case QSPI_FLASH_CMD_SE:
        cmdReg |= QSPI_CMD_PORT_ADDR_SEL(QSPI_ADDR_SEL_3);
        cmdReg |= QSPI_CMD_PORT_CMD_ADDR(0x1);
        QSPI_CMD_ADDR_WRITE(pCtrl, addr);
        break;
    case QSPI_FLASH_CMD_4SE:
        cmdReg |= QSPI_CMD_PORT_ADDR_SEL(QSPI_ADDR_SEL_4);
        cmdReg |= QSPI_CMD_PORT_CMD_ADDR(0x1);
        QSPI_CMD_ADDR_WRITE(pCtrl, addr);
        break;
    case QSPI_FLASH_CMD_P4E:
        cmdReg |= QSPI_CMD_PORT_ADDR_SEL(QSPI_ADDR_SEL_3);
        cmdReg |= QSPI_CMD_PORT_CMD_ADDR(0x1);
        QSPI_CMD_ADDR_WRITE(pCtrl, addr);
        break;
    case QSPI_FLASH_CMD_4P4E:
        cmdReg |= QSPI_CMD_PORT_ADDR_SEL(QSPI_ADDR_SEL_4);
        cmdReg |= QSPI_CMD_PORT_CMD_ADDR(0x1);
        QSPI_CMD_ADDR_WRITE(pCtrl, addr);
        break;
    case QSPI_FLASH_CMD_BE:
        cmdReg |= QSPI_CMD_PORT_ADDR_SEL(QSPI_ADDR_SEL_3);
        break;
    case QSPI_FLASH_CMD_4BE:
        cmdReg |= QSPI_CMD_PORT_ADDR_SEL(QSPI_ADDR_SEL_4);
        break;
    default:
        ret |= QSPI_NOT_SUPPORT;
        return ret;
        break;
    }

    QSPI_CMD_WRITE(pCtrl, cmdReg);
    QSPI_LD_WRITE(pCtrl, 0x0);
    ret |= QSpiWaitForCmd(pCtrl);

    return ret;
}

u32 NorFlashWriteReg(QSpiCtrl *pCtrl, u32 opCode, const u8 *pBuf, size_t len)
{
    FT_ASSERTZERONUM(pCtrl);
    u32 ret = QSPI_SUCCESS;
    u32 cmdReg = 0;

    if (FT_COMPONENT_IS_READY != pCtrl->isReady)
    {
        QSPI_ERROR("Nor flash not ready !!!");
        ret |= QSPI_NOT_READY;
        return ret;
    }

    switch (opCode)
    {
    case QSPI_FLASH_CMD_WREN:
        QSpiEnableWrite(pCtrl);
        return ret;
        break;
    case QSPI_FLASH_CMD_WRDI:
        QSpiDisableWrite(pCtrl);
        return ret;
        break;
    default:
        break;
    }

    cmdReg |= QSPI_CMD_PORT_CMD_MASK &
              QSPI_CMD_PORT_CMD(opCode);
    cmdReg |= QSPI_CMD_PORT_CLK_SEL_MASK &
              QSPI_CMD_PORT_CLK_SEL(pCtrl->config.clkDiv);
    cmdReg |= QSPI_CMD_PORT_CS_MASK &
              QSPI_CMD_PORT_CS(pCtrl->config.channel);

    if ((len > 8) || (NULL == pBuf))
    {
        QSPI_ERROR("data length exceed. commad 0x%lx, len:%d \n", opCode, len);
        ret |= QSPI_INVALID_PARM;
        return ret;
    }
    else if (len > 0)
    {
        cmdReg |= QSPI_CMD_PORT_RW_NUM_MASK &&
                  QSPI_CMD_PORT_RW_NUM(len - 1);
        cmdReg |= QSPI_CMD_PORT_DATA_TRANS(0x1);
    }

    QSPI_CMD_WRITE(pCtrl, cmdReg);
    ret |= QSpiSetLdPortData(pCtrl, pBuf, len);
    return ret;
}

u32 NorFlashInit(QSpiCtrl *pCtrl, const QSpiConfig *pConfig)
{
    FT_ASSERTZERONUM(pCtrl && pConfig);
    u32 ret = QSPI_SUCCESS;

    if (pConfig != &pCtrl->config)
        pCtrl->config = *pConfig;

    QSpiReset(pCtrl);

    pCtrl->isReady = FT_COMPONENT_IS_READY;

    switch (pConfig->capacity)
    {
    case QSPI_FLASH_CAP_4MB:
        pCtrl->flashSize = SZ_4M;
        break;
    case QSPI_FLASH_CAP_8MB:
        pCtrl->flashSize = SZ_8M;
        break;
    case QSPI_FLASH_CAP_16MB:
        pCtrl->flashSize = SZ_16M;
        break;
    case QSPI_FLASH_CAP_32MB:
        pCtrl->flashSize = SZ_32M;
        break;
    case QSPI_FLASH_CAP_64MB:
        pCtrl->flashSize = SZ_64M;
        break;
    case QSPI_FLASH_CAP_128MB:
        pCtrl->flashSize = SZ_128M;
        break;
    case QSPI_FLASH_CAP_256MB:
        pCtrl->flashSize = SZ_256M;
        break;
    default:
        ret = QSPI_NOT_SUPPORT;
        break;
    }

    return ret;
}
